#include "gtest/gtest.h"

#include "Utility/FileSystem/SGFileSystem.h"

#include "DataStructure/Common/Status.h"
#include "DataStructure/Input/Element/CTRIA6Data.h"
#include "DataStructure/Input/Element/ElementTypes.h"

#include "DBManager/IService/Input/Element/IElementService.h"
#include "DBManager/IService/Input/Node/INodeService.h"

#include "Import/ImportAPI/ImportAPI.h"

#include "../../LSectionNumericalCalculator.h"
#include "../SectionCalculator.h"


using SG::Algebra::Point;
using SG::DataStructure::Common::Real;
using SG::DataStructure::Common::Status;
using SG::DataStructure::Common::TOLERANCE;
using SG::DataStructure::FEM::ElementTypes;
using SG::FEMSolver::Element::LSectionNumericalCalculator;
using SG::FEMSolver::Element::SectionCalculator;
using SG::Import::SGImporter;
using namespace SG::DBManager;
using namespace SG::DataStructure::FEM;
using namespace SG::FEMSolver::Element;

extern std::shared_ptr<DBServiceFactory> DB_SERVICEFACTORY;

// L字形截面属性计算测试模型
/*  对标ANSYS:
    ANSYS结果（5级加密）:
    Area                 =  48.000
    Iyy                  =  512.00
    Iyz                  = -192.00
    Izz                  =  272.00
    Warping Constant     =  578.43
    Torsion Constant     =  142.81
    Centroid Y           =  2.0000
    Centroid Z           =  2.0000
    Shear Center Y       = 0.54985
    Shear Center Z       = 0.57068E-01
    Shear Correction-xy  = 0.57103
    Shear Correction-yz  = 0.38635E-01
    Shear Correction-xz  = 0.51031
*/
TEST (SectionCalculatorTest, ComputeLSectionPop)
{
    // 网格划分，引入LSectionNumericalCalculator
    PBARLData section;
    section.SetBeamSectionStandardTypes (BeamSectionStandardTypes::BEAMSECTYPE_L);
    section.m_dimension = { 8.0, 12.0, 4.0, 2.0 };
    int                         refineLevel{ 5 };
    Meshes meshes{};
    LSectionNumericalCalculator eleCalculator;
    eleCalculator.Mesh (section, _OUT meshes, refineLevel);

    // 计算属性
    BeamMeshedSectionProperty sectionProperty{};
    SectionCalculator::Calculate (meshes, _OUT sectionProperty);

    // 测试  对标ANSYS
    ASSERT_NEAR (sectionProperty.m_area, 48, TOLERANCE);
    ASSERT_NEAR (sectionProperty.m_Iy, 512.00, TOLERANCE);
    ASSERT_NEAR (sectionProperty.m_Iz, 272.00, TOLERANCE);
    ASSERT_NEAR (sectionProperty.m_Iyz, -192.00, TOLERANCE);
    ASSERT_NEAR (sectionProperty.m_J, 142.81, 142.81 * 1e-4);
    ASSERT_NEAR (sectionProperty.m_yS, 0.54985 - 2.0, 1e-5);
    ASSERT_NEAR (sectionProperty.m_zS, 0.057068 - 2.0, 1e-5);
    SG::Algebra::Matrixd K = sectionProperty.ComputeK ();
    ASSERT_NEAR (K (0, 0), 0.57103, 0.57103 * 3e-3);
    ASSERT_NEAR (K (1, 1), 0.51031, 0.51031 * 4e-4);

    // 测试  对标NASTRAN
    ASSERT_NEAR (sectionProperty.m_area, 48, TOLERANCE);
    ASSERT_NEAR (sectionProperty.m_Iy, 512.00, TOLERANCE);
    ASSERT_NEAR (sectionProperty.m_Iz, 272.00, TOLERANCE);
    ASSERT_NEAR (sectionProperty.m_Iyz, -192.00, TOLERANCE);
    ASSERT_NEAR (sectionProperty.m_J, 142.94171142578125, 142 * 1e-3);
    ASSERT_NEAR (sectionProperty.m_yS, -1.4506500959396362, 5e-4);
    ASSERT_NEAR (sectionProperty.m_zS, -1.9432718753814697, 4e-4);
    ASSERT_NEAR (K (0, 0), 0.5708415508270264, 0.57 * 1e-4);
    ASSERT_NEAR (K (1, 1), 0.5100651979446411, 0.51 * 2e-4);
}

// 测试与NASTRAN截面网格划分相同情况下的属性计算结果精度
TEST (SectionCalculatorTest, ComputeArbitrarySectionPop)
{
    // 读取BDF网格文件
    auto fileName = SG::Utility::FileSystem::getTestDataPath ("SGFem/BeamSecPropCalculator/") + "sol_101_cbar-pbrsect_gs_inp2_br31.bdf";
    std::string dbName = "ArbitrarySection";
    DB_SERVICEFACTORY->ClearDatabase ();
    SGImporter import (DB_SERVICEFACTORY);
    auto       status = import.Import (fileName);
    if (status == Status::STATUS_Failure)
    {
        std::cerr << "Import Failure !!!" << std::endl;
        throw std::runtime_error ("Failed to import BDF file");
    }

    // 从数据库中读取节点
    const auto&            nodeService = DB_SERVICEFACTORY->get<INodeService> ();
    const auto             nodeSize    = nodeService->Size ();
    NodeSet nodes;
    nodes.reserve (nodeSize);
    SG::DataStructure::FEM::NodeData tempNode;
    Point2D currPoint2D;
    for (std::size_t iNode{ 0 }; iNode < nodeSize; ++iNode)
    {
        nodeService->FindByIndex (iNode, tempNode);
        currPoint2D.nodeId = tempNode.m_id;
        currPoint2D.y      = tempNode.m_x[2];
        currPoint2D.z      = tempNode.m_x[1];
        nodes.emplace_back (currPoint2D);
    }

    // 从数据库中读取单元信息
    const auto&                        elementService = DB_SERVICEFACTORY->get<IElementService> ();
    const auto                         eleSize        = elementService->GetElementSize (ELETYPE_ShellTria6);
    SG::DataStructure::FEM::CTRIA6Data tempCTRIA6;
    ElementSet elements;
    elements.reserve (eleSize);
    Element currEle;
    currEle.nodes.reserve (6);
    for (std::size_t iEle{ 0 }; iEle < eleSize; ++iEle)
    {
        elementService->FindByIndex (iEle, tempCTRIA6);
        currEle.eleId = tempCTRIA6.m_id;
        currEle.nodes = { static_cast<int> (tempCTRIA6.m_g[0] - 10000), static_cast<int> (tempCTRIA6.m_g[1] - 10000),
                          static_cast<int> (tempCTRIA6.m_g[2] - 10000), static_cast<int> (tempCTRIA6.m_g[3] - 10000),
                          static_cast<int> (tempCTRIA6.m_g[4] - 10000), static_cast<int> (tempCTRIA6.m_g[5] - 10000) };
        elements.emplace_back (currEle);
    }

    // 构建网格
    Meshes meshes{ nodes, {}, elements };

    // 计算属性
    BeamMeshedSectionProperty property{};
    SectionCalculator::Calculate (meshes, _OUT property);

    // 测试
    ASSERT_NEAR (property.m_area, 92.19999999999999, 92.0 * TOLERANCE);
    ASSERT_NEAR (property.m_Iy, 4705.93896484375, 4705.0 * TOLERANCE);
    ASSERT_NEAR (property.m_Iz, 4481.912109375, 4481.0 * TOLERANCE);
    ASSERT_NEAR (property.m_Iyz, 0, TOLERANCE);
    ASSERT_NEAR (property.m_J, 6734.27880859375, 6734.0 * TOLERANCE);
    SG::Algebra::Matrixd K = property.ComputeK ();
    ASSERT_NEAR (K (1, 1), 0.5601521134376526, 0.56 * TOLERANCE);
    ASSERT_NEAR (K (0, 0), 0.35838454961776733, 0.35 * TOLERANCE);
}